/*
 * Copyright (c) 2021 Futurewei Technologies, Inc.
 *
 * clang2mpl is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan
 * PSL v2. You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the
 * Mulan PSL v2 for more details.
 */
// RUN: %clang2mpl --ascii --verify %s -- -Wno-unused-value --target=aarch64-linux-elf
// RUN: cat %m | %FileCheck %s
#include <stdarg.h>

int add(int count, ...) {
  va_list ap;
  int i, sum = 0;

  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: intrinsiccall C_va_start (addrof a64 %ap, dread i32 %count)
  va_start(ap, count);
  for (i = 0; i < count; i++)
    // CHECK: LOC 2 [[# @LINE + 16 ]]{{$}}
    // CHECK-NEXT: dassign %va_offs.[[#OFFS:]] 0 (dread i32 %ap 4)
    // CHECK-NEXT: brtrue @_vaarg_on_stack.[[#STACKLBL:]] (ge i32 i32 (dread i32 %va_offs.[[#OFFS]], constval i32 0))
    // CHECK-NEXT: dassign %ap 4 (add i32 (dread i32 %va_offs.[[#OFFS]], constval i32 8))
    // CHECK-NEXT: brtrue @_vaarg_on_stack.[[#STACKLBL]] (gt i32 i32 (dread i32 %ap 4, constval i32 0))
    // CHECK-NEXT: dassign %va_arg_ptr.[[#ARGPTR:]] 0 (add a64 (
    // CHECK-NEXT:     dread a64 %ap 2,
    // CHECK-NEXT:     cvt a64 i32 (dread i32 %va_offs.[[#OFFS]])))
    // CHECK-NEXT: goto @_vaarg_end.[[#ENDLBL:]]
    // CHECK-NEXT: @_vaarg_on_stack.[[#STACKLBL]]     dassign %va_arg_ptr.[[#ARGPTR]] 0 (dread u64 %ap 1)
    // CHECK-NEXT: dassign %ap 1 (band u64 (
    // CHECK-NEXT:     add u64 (dread u64 %va_arg_ptr.[[#ARGPTR]], constval u64 11),
    // CHECK-NEXT:     constval u64 -8))
    // CHECK-NEXT: @_vaarg_end.[[#ENDLBL]]     dassign %sum 0 (add i32 (
    // CHECK-NEXT:     dread i32 %sum,
    // CHECK-NEXT:     iread i32 <* i32> 0 (dread a64 %va_arg_ptr.[[#ARGPTR]])))
    sum += va_arg(ap, int);

  va_end(ap);
  return sum;
}

long longAdd(int count, ...) {
  va_list ap;
  long i, sum = 0;

  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: intrinsiccall C_va_start (addrof a64 %ap, dread i32 %count)
  va_start(ap, count);
  for (i = 0; i < count; i++)
    // CHECK: LOC 2 [[# @LINE + 16 ]]{{$}}
    // CHECK-NEXT: dassign %va_offs.[[#OFFS:]] 0 (dread i32 %ap 4)
    // CHECK-NEXT: brtrue @_vaarg_on_stack.[[#STACKLBL:]] (ge i32 i32 (dread i32 %va_offs.[[#OFFS]], constval i32 0))
    // CHECK-NEXT: dassign %ap 4 (add i32 (dread i32 %va_offs.[[#OFFS]], constval i32 8))
    // CHECK-NEXT: brtrue @_vaarg_on_stack.[[#STACKLBL]] (gt i32 i32 (dread i32 %ap 4, constval i32 0))
    // CHECK-NEXT: dassign %va_arg_ptr.[[#ARGPTR:]] 0 (add a64 (
    // CHECK-NEXT:     dread a64 %ap 2,
    // CHECK-NEXT:     cvt a64 i32 (dread i32 %va_offs.[[#OFFS]])))
    // CHECK-NEXT: goto @_vaarg_end.[[#ENDLBL:]]
    // CHECK-NEXT: @_vaarg_on_stack.[[#STACKLBL]]     dassign %va_arg_ptr.[[#ARGPTR]] 0 (dread u64 %ap 1)
    // CHECK-NEXT: dassign %ap 1 (band u64 (
    // CHECK-NEXT:     add u64 (dread u64 %va_arg_ptr.[[#ARGPTR]], constval u64 15),
    // CHECK-NEXT:     constval u64 -8))
    // CHECK-NEXT: @_vaarg_end.[[#ENDLBL]]     dassign %sum 0 (add i64 (
    // CHECK-NEXT:     dread i64 %sum,
    // CHECK-NEXT:     iread i64 <* i64> 0 (dread a64 %va_arg_ptr.[[#ARGPTR]])))
    sum += va_arg(ap, long);

  va_end(ap);
  return sum;
}

 double floatAdd(int count, ...) {
  va_list ap;
  int i;
  double sum = 0.0;

  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: intrinsiccall C_va_start (addrof a64 %ap, dread i32 %count)
  va_start(ap, count);
  for (i = 0; i < count; i++)
    // CHECK: LOC 2 [[# @LINE + 16 ]]{{$}}
    // CHECK-NEXT: dassign %va_offs.[[#OFFS:]] 0 (dread i32 %ap 5)
    // CHECK-NEXT: brtrue @_vaarg_on_stack.[[#STACKLBL:]] (ge i32 i32 (dread i32 %va_offs.[[#OFFS]], constval i32 0))
    // CHECK-NEXT: dassign %ap 5 (add i32 (dread i32 %va_offs.[[#OFFS]], constval i32 16))
    // CHECK-NEXT: brtrue @_vaarg_on_stack.[[#STACKLBL]] (gt i32 i32 (dread i32 %ap 5, constval i32 0))
    // CHECK-NEXT: dassign %va_arg_ptr.[[#ARGPTR:]] 0 (add a64 (
    // CHECK-NEXT:     dread a64 %ap 3,
    // CHECK-NEXT:     cvt a64 i32 (dread i32 %va_offs.[[#OFFS]])))
    // CHECK-NEXT: goto @_vaarg_end.[[#ENDLBL:]]
    // CHECK-NEXT: @_vaarg_on_stack.[[#STACKLBL]]     dassign %va_arg_ptr.[[#ARGPTR]] 0 (dread u64 %ap 1)
    // CHECK-NEXT: dassign %ap 1 (band u64 (
    // CHECK-NEXT:     add u64 (dread u64 %va_arg_ptr.[[#ARGPTR]], constval u64 15),
    // CHECK-NEXT:     constval u64 -8))
    // CHECK-NEXT: @_vaarg_end.[[#ENDLBL]]     dassign %sum 0 (add f64 (
    // CHECK-NEXT:     dread f64 %sum,
    // CHECK-NEXT:     iread f64 <* f64> 0 (dread a64 %va_arg_ptr.[[#ARGPTR]])))
    sum += va_arg(ap, double);

  va_end(ap);
  return sum;
}

struct S1 {
  int a;
  int b;
};

int s1Add(int count, ...) {
    va_list ap;
  int i, sum = 0;

  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: intrinsiccall C_va_start (addrof a64 %ap, dread i32 %count)
  va_start(ap, count);
  for (i = 0; i < count; i++) {
    // CHECK: LOC 2 [[# @LINE + 14 ]]{{$}}
    // CHECK-NEXT: dassign %va_offs.[[#OFFS:]] 0 (dread i32 %ap 4)
    // CHECK-NEXT: brtrue @_vaarg_on_stack.[[#STACKLBL:]] (ge i32 i32 (dread i32 %va_offs.[[#OFFS]], constval i32 0))
    // CHECK-NEXT: dassign %ap 4 (add i32 (dread i32 %va_offs.[[#OFFS]], constval i32 8))
    // CHECK-NEXT: brtrue @_vaarg_on_stack.[[#STACKLBL]] (gt i32 i32 (dread i32 %ap 4, constval i32 0))
    // CHECK-NEXT: dassign %va_arg_ptr.[[#ARGPTR:]] 0 (add a64 (
    // CHECK-NEXT:     dread a64 %ap 2,
    // CHECK-NEXT:     cvt a64 i32 (dread i32 %va_offs.[[#OFFS]])))
    // CHECK-NEXT: goto @_vaarg_end.[[#ENDLBL:]]
    // CHECK-NEXT: @_vaarg_on_stack.[[#STACKLBL]]     dassign %va_arg_ptr.[[#ARGPTR]] 0 (dread u64 %ap 1)
    // CHECK-NEXT: dassign %ap 1 (band u64 (
    // CHECK-NEXT:     add u64 (dread u64 %va_arg_ptr.[[#ARGPTR]], constval u64 15),
    // CHECK-NEXT:     constval u64 -8))
    // CHECK-NEXT: @_vaarg_end.[[#ENDLBL]]     dassign %s 0 (iread agg <* <$S1>> 0 (dread a64 %va_arg_ptr.[[#ARGPTR]]))
    struct S1 s = va_arg(ap, struct S1);
    // CHECK: LOC 2 [[# @LINE + 4 ]]{{$}}
    // CHECK-NEXT: dassign %sum 0 (add i32 (
    // CHECK-NEXT:     dread i32 %sum,
    // CHECK-NEXT:     add i32 (dread i32 %s 1, dread i32 %s 2)))
    sum += (s.a + s.b);
  }

  va_end(ap);
  return sum;
}
